﻿#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Rail.Net.Common;
using Rail.Net.Common.Extensions;
using Rail.Net.Core.Model.Nonpersistent.Map;

#endregion

namespace Rail.Net.Core.Model.Nonpersistent
{
    /// <summary>
    ///   Represents a model for single connection/route between the Departure and Arrival station.
    ///   It contains information like: Direction (forward / return), First station, Last station, Transfer stations, Border station(s)
    /// </summary>
    [Serializable]
    [DataContract]
    public class ConnectionModel : IEquatable<ConnectionModel>
    {
        #region Fields

        private List<StationModel> _allBorderStations;
        private List<StationModel> _allStations;

        private List<SequenceModel> _sequences;
        private List<SequenceModel> _contengentSequences;
        private List<SequenceModel> _globalSequences;

        private List<StationModel> _transferStations;

        private TrainModel _contengentTrain;
        private List<TrainModel> _globalTrains;
        private List<TrainModel> _reservableTrains;
        private List<TrainModel> _virtualRuTrains;

        #endregion

        #region Constructors

        public ConnectionModel(List<BorderModel> availableBorders,
                               IEnumerable<ContengentTrainLabel> contengentTrainLabels,
                               IEnumerable<ContengentTrainNumber> contengentTrainNumbers,
                               IEnumerable<GlobalTrainLabel> globalTrainLabels,
                               IEnumerable<GlobalTrainNumber> globalTrainNumbers,
                               IEnumerable<ReservableTrainLabel> reservableTrainLabels,
                               IEnumerable<ReservableTrainNumber> reservableTrainNumbers,
                               IEnumerable<VirtualRuTrainNumber> virtualRuTrainNumbers
            )
        {
            Trains = new List<TrainModel>();

            IsValid = true;
            _allStations = null;
            _allBorderStations = null;
            _transferStations = null;

            _sequences = null;
            _contengentSequences = null;
            _globalSequences = null;

            _contengentTrain = null;
            _globalTrains = null;
            _reservableTrains = null;
            _virtualRuTrains = null;

            AvailableBorders = availableBorders;

            ContengentTrainLabels = contengentTrainLabels;
            ContengentTrainNumbers = contengentTrainNumbers;
            GlobalTrainLabels = globalTrainLabels;
            GlobalTrainNumbers = globalTrainNumbers;
            ReservableTrainLabels = reservableTrainLabels;
            ReservableTrainNumbers = reservableTrainNumbers;
            VirtualRuTrainNumbers = virtualRuTrainNumbers;
        }

        #endregion

        #region Properties

        /// <summary>
        ///   Id
        /// </summary>
        [DataMember]
        public int Id { get; set; }

        /// <summary>
        ///   Is the connection valid (if there is a corresponding relation mapping)
        /// </summary>
        [DataMember]
        public bool IsValid { get; set; }

        /// <summary>
        ///   Number of transfers
        /// </summary>
        [DataMember]
        public string Transfers { get; set; }

        /// <summary>
        ///   Duration of the journey
        /// </summary>
        [DataMember]
        public TimeSpan Duration { get; set; }

        /// <summary>
        ///   Departure time
        /// </summary>
        [DataMember]
        public DateTime DepartureTime { get; set; }

        /// <summary>
        ///   Arrival time
        /// </summary>
        [DataMember]
        public DateTime ArrivalTime { get; set; }

        /// <summary>
        ///   Train list
        /// </summary>
        [DataMember]
        public List<TrainModel> Trains { get; set; }

        [DataMember]
        public List<BorderModel> AvailableBorders { get; set; }

        /// <summary>
        ///   Route description of all connection stations
        /// </summary>
        public string Route
        {
            get
            {
                var result = new StringBuilder();
                if (Trains != null)
                {
                    Trains.ForEach(s => result.Append(s.Route));
                }
                return result.ToString();
            }
        }

        /// <summary>
        ///   First station
        /// </summary>
        public StationModel FirstStation
        {
            get { return Trains.SelectMany(t => t.Steps).First(); }
        }

        /// <summary>
        ///   Last station
        /// </summary>
        public StationModel LastStation
        {
            get { return Trains.SelectMany(t => t.Steps).Last(); }
        }

        /// <summary>
        ///   All border station found in the route
        /// </summary>
        public List<StationModel> AllBorderStations
        {
            get
            {
                if (_allBorderStations == null)
                {
                    PrepareConnection();
                }
                return _allBorderStations;
            }
            set { _allBorderStations = value; }
        }

        /// <summary>
        ///   All stations in the route
        /// </summary>
        public List<StationModel> AllStations
        {
            get
            {
                if (_allStations == null)
                {
                    PrepareConnection();
                }
                return _allStations;
            }
            set { _allStations = value; }
        }

        /// <summary>
        ///   All transfer station (where the train changes)
        /// </summary>
        public List<StationModel> TransferStations
        {
            get
            {
                if (_transferStations == null)
                {
                    PrepareConnection();
                }
                return _transferStations;
            }
            set { _transferStations = value; }
        }

        /// <summary>
        ///   All sequences
        /// </summary>
        [DataMember]
        public List<SequenceModel> Sequences
        {
            get
            {
                if (_sequences == null)
                {
                    PrepareConnection();
                }
                return _sequences;
            }
            set { _sequences = value; }
        }

        /// <summary>
        /// Domestic sequences
        /// </summary>
        public List<DomesticSequenceModel> DomesticSequences
        {
            get { return Sequences.OfType<DomesticSequenceModel>().ToList(); }
        }

        /// <summary>
        /// International sequences
        /// </summary>
        public List<InternationalSequenceModel> InternationalSequences
        {
            get { return Sequences.OfType<InternationalSequenceModel>().ToList(); }
        }

        /// <summary>
        ///   All contengent sequences
        /// </summary>
        [DataMember]
        public List<SequenceModel> ContengentSequences
        {
            get
            {
                if (_contengentSequences == null)
                {
                    PrepareConnection();
                }
                return _contengentSequences;
            }
            set { _contengentSequences = value; }
        }

        /// <summary>
        ///   All global prices sequences (domestic & global trains)
        /// </summary>
        [DataMember]
        public List<SequenceModel> GlobalSequences
        {
            get
            {
                if (_globalSequences == null)
                {
                    PrepareConnection();
                }
                return _globalSequences;
            }
            set { _globalSequences = value; }
        }

        public List<GlobalSequenceModel> GlobalOnlySequences
        {
            get { return GlobalSequences.OfType<GlobalSequenceModel>().ToList(); }
        }

        public List<DomesticSequenceModel> GlobalDomesticSequences
        {
            get { return GlobalSequences.OfType<DomesticSequenceModel>().ToList(); }
        }

        public List<InternationalSequenceModel> GlobalInternationalSequences
        {
            get { return GlobalSequences.OfType<InternationalSequenceModel>().ToList(); }
        }

        /// <summary>
        ///   Train labels included in the contengent products
        /// </summary>
        [DataMember]
        public IEnumerable<ContengentTrainLabel> ContengentTrainLabels { get; private set; }

        /// <summary>
        ///   Train numbers included/excluded in the contengent products
        /// </summary>
        [DataMember]
        public IEnumerable<ContengentTrainNumber> ContengentTrainNumbers { get; private set; }

        /// <summary>
        ///   Train labels included in the global price products
        /// </summary>
        [DataMember]
        public IEnumerable<GlobalTrainLabel> GlobalTrainLabels { get; private set; }

        /// <summary>
        ///   Train numbers included/excluded in the global price products
        /// </summary>
        [DataMember]
        public IEnumerable<GlobalTrainNumber> GlobalTrainNumbers { get; private set; }

        /// <summary>
        ///   Train labels included for reservable trains
        /// </summary>
        [DataMember]
        public IEnumerable<ReservableTrainLabel> ReservableTrainLabels { get; private set; }

        /// <summary>
        ///   Train labels included/excluded for reservable trains
        /// </summary>
        [DataMember]
        public IEnumerable<ReservableTrainNumber> ReservableTrainNumbers { get; private set; }

        /// <summary>
        /// Virtual Ru train numbers
        /// </summary>
        [DataMember]
        public IEnumerable<VirtualRuTrainNumber> VirtualRuTrainNumbers { get; private set; }

        public TrainModel ContengentTrain
        {
            get
            {
                if (_contengentTrain == null)
                {
                    if (IsContengentEligable)
                    {
                        // if it is domestic only connection, then check the look up tables, otherwise find the first train that crosses the border
                        if (IsDomesticOnly)
                        {
                            var contengentLabels =
                                ContengentTrainLabels.Where(
                                    l =>
                                    (IsDomesticOnly ? l.Domestic : l.International) &&
                                    (!l.ValidFrom.HasValue || l.ValidFrom <= DateTime.Now) &&
                                    (!l.ValidTo.HasValue || l.ValidTo >= DateTime.Now)).Select(
                                        l => l.TrainLabel.ToLower());

                            var contengentNumbersIncluded = ContengentTrainNumbers.Where(
                                n =>
                                (IsDomesticOnly ? n.Domestic : n.International) && n.Include &&
                                (!n.ValidFrom.HasValue || n.ValidFrom <= DateTime.Now) &&
                                (!n.ValidTo.HasValue || n.ValidTo >= DateTime.Now)).Select(l => l.TrainNumber.ToLower());

                            var contengentNumbersExcluded = ContengentTrainNumbers.Where(
                                n =>
                                n.Domestic && !n.Include &&
                                (!n.ValidFrom.HasValue || n.ValidFrom <= DateTime.Now) &&
                                (!n.ValidTo.HasValue || n.ValidTo >= DateTime.Now)).Select(l => l.TrainNumber.ToLower());


                            _contengentTrain =
                                Trains.FirstOrDefault(
                                    t =>
                                    (contengentLabels.Contains(t.Label.ToLower()) ||
                                     contengentNumbersIncluded.Contains(t.Number.ToLower())) &&
                                    !contengentNumbersExcluded.Contains(t.Number.ToLower()));
                        }
                        if (AllBorderStations.Count == 1)
                        {
                            _contengentTrain = Trains.FirstOrDefault(t => t.Steps.Contains(AllBorderStations[0]));
                        }
                    }
                }
                return _contengentTrain;
            }
        }

        /// <summary>
        /// Global trains
        /// </summary>
        public List<TrainModel> GlobalTrains
        {
            get
            {
                if (_globalTrains == null)
                {
                    _globalTrains = new List<TrainModel>();
                    var globalLabels =
                        GlobalTrainLabels.Where(
                            l =>
                            (IsDomesticOnly ? l.Domestic : l.International) &&
                            (!l.ValidFrom.HasValue || l.ValidFrom <= DateTime.Now) &&
                            (!l.ValidTo.HasValue || l.ValidTo >= DateTime.Now)).Select(l => l.TrainLabel.ToLower());

                    var globalNumbersIncluded = GlobalTrainNumbers.Where(
                        n =>
                        (IsDomesticOnly ? n.Domestic : n.International) && n.Include &&
                        (!n.ValidFrom.HasValue || n.ValidFrom <= DateTime.Now) &&
                        (!n.ValidTo.HasValue || n.ValidTo >= DateTime.Now)).Select(l => l.TrainNumber.ToLower());

                    var globalNumbersExcluded = GlobalTrainNumbers.Where(
                        n =>
                        n.Domestic && !n.Include &&
                        (!n.ValidFrom.HasValue || n.ValidFrom <= DateTime.Now) &&
                        (!n.ValidTo.HasValue || n.ValidTo >= DateTime.Now)).Select(l => l.TrainNumber.ToLower());

                    _globalTrains.AddRange(Trains.Where(t => (globalLabels.Contains(t.Label.ToLower()) ||
                                              globalNumbersIncluded.Contains(t.Number.ToLower())) &&
                                             !globalNumbersExcluded.Contains(t.Number.ToLower())));
                }
                return _globalTrains;
            }
        }

        /// <summary>
        /// Reservable trains
        /// </summary>
        public List<TrainModel> ReservableTrains
        {
            get
            {
                if (_reservableTrains == null)
                {
                    _reservableTrains = new List<TrainModel>();
                    var reservableLabels =
                        ReservableTrainLabels.Where(
                            l =>
                            (IsDomesticOnly ? l.Domestic : l.International) &&
                            (!l.ValidFrom.HasValue || l.ValidFrom <= DateTime.Now) &&
                            (!l.ValidTo.HasValue || l.ValidTo >= DateTime.Now)).Select(l => l.TrainLabel.ToLower());

                    var reservableNumbersIncluded = ReservableTrainNumbers.Where(
                        n =>
                        (IsDomesticOnly ? n.Domestic : n.International) && n.Include &&
                        (!n.ValidFrom.HasValue || n.ValidFrom <= DateTime.Now) &&
                        (!n.ValidTo.HasValue || n.ValidTo >= DateTime.Now)).Select(l => l.TrainNumber.ToLower());

                    var reservableNumbersExcluded = ReservableTrainNumbers.Where(
                        n =>
                        n.Domestic && !n.Include &&
                        (!n.ValidFrom.HasValue || n.ValidFrom <= DateTime.Now) &&
                        (!n.ValidTo.HasValue || n.ValidTo >= DateTime.Now)).Select(l => l.TrainNumber.ToLower());

                    _reservableTrains.AddRange(Trains.Where(t => (reservableLabels.Contains(t.Label.ToLower()) ||
                                              reservableNumbersIncluded.Contains(t.Number.ToLower())) &&
                                             !reservableNumbersExcluded.Contains(t.Number.ToLower())));
                }
                return _reservableTrains;
            }
        }

        /// <summary>
        /// Virtual RU trains
        /// </summary>
        public List<TrainModel> VirtualRuTrains
        {
            get
            {
                if (_virtualRuTrains == null)
                {
                    _virtualRuTrains = new List<TrainModel>();
                    var validVirtualRuTrainNumbers =
                        VirtualRuTrainNumbers.Where(
                            v =>
                            (!v.ValidFrom.HasValue || v.ValidFrom <= DateTime.Now) &&
                            (!v.ValidTo.HasValue || v.ValidTo >= DateTime.Now));

                    var virtualRuTrainNumbers = validVirtualRuTrainNumbers.Select(v => v.TrainNumber);

                    _virtualRuTrains.AddRange(
                        Trains.Where(
                            t =>
                            (virtualRuTrainNumbers.Contains(t.Number.ToLower()) &&
                             t.Rus.Contains(validVirtualRuTrainNumbers.Single(v => v.TrainNumber == t.Number).Ru.Code))));
                }
                return _virtualRuTrains;
            }
        }

        public bool IsDomesticOnly
        {
            get { return DomesticSequences.Count == 1 && InternationalSequences.Count == 0; }
        }

        public bool IsContengentEligable
        {
            get { return DomesticSequences.Count == 1 && InternationalSequences.Count <= 1; }
        }

        public bool IsGlobalEligable
        {
            get { return GlobalSequences.OfType<GlobalSequenceModel>().Count() > 0; }
        }

        public double MapCenterX
        {
            get { return AllStations.Average(station => Double.Parse(station.X)/1000000); }
        }

        public double MapCenterY
        {
            get { return AllStations.Average(station => Double.Parse(station.Y)/1000000); }
        }

        public IEnumerable<MarkerModel> MapMarkers
        {
            get
            {
                try
                {
                    return AllStations.Select(s => new MarkerModel
                                                       {
                                                           lat = Double.Parse(s.Y)/1000000,
                                                           lng = Double.Parse(s.X)/1000000,
                                                           data = String.Format("{0} ({1})", s.Name, s.Eva),
                                                           tag = string.Format("{0} ({1})", s.Name , s.Eva)
                                                       }
                        ).ToList();
                }
                catch (Exception e)
                {
                    return new List<MarkerModel>();
                }
            }
        }

        public List<string> StationTags
        {
            get { return MapMarkers.Select(s => s.tag).ToList(); }
        }

        public List<PolylineModel> MapLines
        {
            get
            {
                var result = new List<PolylineModel>();

                try
                {
                    var index = 0;
                    foreach (var train in Trains)
                    {
                        var polyline = new PolylineModel(index++)
                                           {name = string.Format("{0} {1}", train.Label, train.Number)};
                        foreach (var station in train.Steps)
                        {
                            polyline.path.Add(new PolylineData
                                                  {
                                                      lng = Double.Parse(station.X)/1000000,
                                                      lat = Double.Parse(station.Y)/1000000
                                                  });
                        }
                        result.Add(polyline);
                    }
                }
                catch (Exception e)
                {
                    var x = 0;
                }


                return result;
            }
        }

        public List<string> TrainTags
        {
            get { return MapLines.Select(t => t.name).ToList(); }
        }

        public Gmap3Model Gmap3
        {
            get { return new Gmap3Model(MapLines); }
        }

        #endregion

        #region Methods

        private void PrepareConnection()
        {
            // find the border station
            var borderList = new List<StationModel>();
            var stationList = new List<StationModel>();
            var transferList = new List<StationModel>();

            // calculate the list of all stations and the list of possible border stations
            foreach (var step in Trains.SelectMany(train => train.Steps))
            {
                if (!stationList.Contains(step))
                {
                    stationList.Add(step);
                }
                if (AvailableBorders.Any(b => b.Eva == step.Eva) && !borderList.Contains(step))
                {
                    borderList.Add(step);
                }
            }

            // create the list of transfer stations (train change) - skip the last train and take only the train arrival stations
            transferList.AddRange(
                Trains.Take(Trains.Count - 1).Select(
                    (train, index) => stationList.SingleOrDefault(s => s.Eva == train.Arrival.Eva)));

            // remove the border stations in which there is no change of RU in any of the previous or next station
            for (var i = 0; i < borderList.Count; i++)
            {
                var previousBorderIndex = (i != 0 ? stationList.IndexOf(borderList[i - 1]) : -1);
                var nextBorderIndex = (i != borderList.Count - 1
                                           ? stationList.IndexOf(borderList[i + 1])
                                           : stationList.Count);
                var borderIndex = stationList.IndexOf(borderList[i]);

                var border = borderList[i];
                //var previousBorder = (i != 0 ? borderList[i - 1] : null);
                //var nextBorder = (i != borderList.Count - 1 ? borderList[i - 1] : null);

                var previousStations =
                    stationList.Skip(previousBorderIndex + 1).Take(borderIndex - previousBorderIndex - 1).ToList();
                var nextStations = stationList.Skip(borderIndex + 1).Take(nextBorderIndex - borderIndex - 1).ToList();

                var ruChanged = false;
                for (var j = 0; j < 4; j++)
                {
                    if (j < previousStations.Count)
                    {
                        if ((border.Ru == Constants.DomesticRu &&
                             previousStations[previousStations.Count - 1 - j].Ru != Constants.DomesticRu) ||
                            (border.Ru != Constants.DomesticRu &&
                             previousStations[previousStations.Count - 1 - j].Ru == Constants.DomesticRu))
                        {
                            ruChanged = true;
                        }
                    }
                    if (j < nextStations.Count)
                    {
                        if ((border.Ru == Constants.DomesticRu && nextStations[j].Ru != Constants.DomesticRu) ||
                            (border.Ru != Constants.DomesticRu && nextStations[j].Ru == Constants.DomesticRu))
                        {
                            ruChanged = true;
                        }
                    }
                }
                if (!ruChanged)
                {
                    borderList.Remove(border);
                    i = -1;
                }
            }


            // after cleaning the non-border stations
            // set the border model to them
            borderList.ForEach(b => b.BorderModel = AvailableBorders.Single(ab => ab.Eva == b.Eva));


            _allStations = stationList;
            _allBorderStations = borderList;
            _transferStations = transferList;


            _sequences = new List<SequenceModel>();
            _contengentSequences = new List<SequenceModel>();
            _globalSequences = new List<SequenceModel>();

            // calculate the domestic and international sequences
            var keyStations = new List<StationModel> {FirstStation};
            foreach (var t in borderList.Where(t => !keyStations.Contains(t)))
            {
                keyStations.Add(t);
            }
            //foreach (var virtualRuTrain in VirtualRuTrains)
            //{
            //    if (!keyStations.Contains(virtualRuTrain.Steps.First()))
            //    {
            //        keyStations.Add(virtualRuTrain.Steps.First());
            //    }
            //    if (!keyStations.Contains(virtualRuTrain.Steps.Last()))
            //    {
            //        keyStations.Add(virtualRuTrain.Steps.Last());
            //    }
            //}
            if (!keyStations.Contains(LastStation))
            {
                keyStations.Add(LastStation);
            }
            //keyStations = keyStations.OrderBy(s => s.Id).ToList();


            // domestic and international
            var seqIndex = 0;
            var keyPairs = keyStations.ConsecutivePairs();
            foreach (var keyPair in keyPairs)
            {
                var firstIndex = stationList.IndexOf(keyPair.Item1);
                var secondIndex = stationList.IndexOf(keyPair.Item2);

                var keyList = new List<StationModel>();
                keyList.AddRange(stationList.Skip(firstIndex).Take(secondIndex - firstIndex + 1));

                var first = keyList.First();
                var last = keyList.Last();

                if ((first.IsDomestic && !first.IsBorder && last.IsDomestic && !last.IsBorder) ||                   // if {d-d, b-d, d-b}
                    (first.IsDomestic && !first.IsBorder && last.IsBorder) ||
                    (first.IsBorder && last.IsDomestic && !last.IsBorder))
                {
                    _sequences.Add(new DomesticSequenceModel(this)
                                       {Sequence = new SortedSet<StationModel>(keyList), Index = seqIndex});
                }
                else if ((first.IsInternational && !first.IsBorder && last.IsInternational && !last.IsBorder) ||    // if {i-i, b-i, i-b}
                        (first.IsInternational && !first.IsBorder && last.IsBorder) ||
                        (first.IsBorder && last.IsInternational && !last.IsBorder))
                {
                    _sequences.Add(new InternationalSequenceModel(this)
                                       {Sequence = new SortedSet<StationModel>(keyList), Index = seqIndex});
                }
                else if (first.IsBorder && last.IsBorder)                                                           // if {b-b} search for the representative RU
                {
                    var ruRepresentative =
                        keyList.Where(s => s != first && s != last).GroupBy(s => s.Ru).Select(x => new { Ru = x.Key, Count = x.Count() }).OrderByDescending(
                            x => x.Count).First().Ru;
                    if (ruRepresentative == Constants.DomesticRu)
                    {
                        _sequences.Add(new DomesticSequenceModel(this) { Sequence = new SortedSet<StationModel>(keyList), Index = seqIndex });
                    }
                    else
                    {
                        _sequences.Add(new InternationalSequenceModel(this) { Sequence = new SortedSet<StationModel>(keyList), Index = seqIndex });
                    }
                }
                else
                {
                    _sequences.Add(new MixedSequenceModel(this) { Sequence = new SortedSet<StationModel>(keyList), Index = seqIndex });
                }
                seqIndex++;
            }

            // calculate the contengent sequences
            seqIndex = 0;
            if (ContengentTrain != null)
            {
                _contengentSequences.Add(new ContengentSequenceModel(this)
                {
                    Index = seqIndex,
                    ContengentTrain = ContengentTrain,
                    Sequence = ContengentTrain.StationSet
                });
            }

            // calculate the global price sequences
            var globalSequences = new List<SequenceModel>();
            if (GlobalTrains.Count > 0)
            {
                var sequences = _sequences.OrderBy(s => s.Index).Select(s => s.Clone());
                var lists = new List<List<StationModel>>();
                // intersect every sequence with every train
                foreach (var sequence in sequences)
                {
                    foreach (var globalTrain in GlobalTrains)
                    {
                        if (sequence.Sequence.Count > 0)
                        {
                            var firstG = globalTrain.Steps.First();
                            var lastG = globalTrain.Steps.Last();

                            var keepFirst = sequence.Sequence.Any(s => s.Id == firstG.Id) && sequence.Sequence.Any(s => s.Id == firstG.Id - 1);
                            var keepLast = sequence.Sequence.Any(s => s.Id == lastG.Id) && sequence.Sequence.Any(s => s.Id == lastG.Id + 1);

                            sequence.Sequence.RemoveWhere(
                                s =>
                                ((!keepFirst && s.Id >= firstG.Id) || (keepFirst && s.Id > firstG.Id)) &&
                                ((!keepLast && s.Id <= lastG.Id) || (keepLast && s.Id < lastG.Id)));
                        }
                    }

                    // create one or two lists of the rest stations
                    var list = new List<StationModel>();
                    if (sequence.Sequence.Count > 0)
                    {
                        lists.Add(list);
                        list.Add(sequence.Sequence.ElementAt(0));

                        for (var i = 1; i < sequence.Sequence.Count; i++)
                        {
                            if (sequence.Sequence.ElementAt(i - 1).Id != sequence.Sequence.ElementAt(i).Id - 1)
                            {
                                list = new List<StationModel>();
                                lists.Add(list);
                            }
                            list.Add(sequence.Sequence.ElementAt(i));
                        }
                    }
                }
                seqIndex = 0;
                foreach (var list in lists.Union(GlobalTrains.Select(t => t.Steps)).OrderBy(l => l.First().Id))
                {
                    var first = list.First();
                    var last = list.Last();
                    var globalTrain = GlobalTrains.FirstOrDefault(t => t.Steps.First() == first && t.Steps.Last() == last);
                    if (globalTrain != null)
                    {
                        globalSequences.Add(new GlobalSequenceModel(this)
                                                {Sequence = new SortedSet<StationModel>(list), Index = seqIndex, GlobalTrain = globalTrain});
                    }
                    else if ((first.IsDomestic && !first.IsBorder && last.IsDomestic && !last.IsBorder) ||
                             // if {d-d, b-d, d-b}
                             (first.IsDomestic && !first.IsBorder && last.IsBorder) ||
                             (first.IsBorder && last.IsDomestic && !last.IsBorder))
                    {
                        globalSequences.Add(new DomesticSequenceModel(this)
                                                {Sequence = new SortedSet<StationModel>(list), Index = seqIndex});
                    }
                    else if ((first.IsInternational && !first.IsBorder && last.IsInternational && !last.IsBorder) ||
                             // if {i-i, b-i, i-b}
                             (first.IsInternational && !first.IsBorder && last.IsBorder) ||
                             (first.IsBorder && last.IsInternational && !last.IsBorder))
                    {
                        globalSequences.Add(new InternationalSequenceModel(this)
                                                {Sequence = new SortedSet<StationModel>(list), Index = seqIndex});
                    }
                    else if (first.IsBorder && last.IsBorder) // if {b-b} search for the representative RU
                    {
                        var ruRepresentative =
                            list.Where(s => s != first && s != last).GroupBy(s => s.Ru).Select(x => new { Ru = x.Key, Count = x.Count() }).
                                OrderByDescending(
                                    x => x.Count).First().Ru;
                        if (ruRepresentative == Constants.DomesticRu)
                        {
                            globalSequences.Add(new DomesticSequenceModel(this)
                                                    {
                                                        Sequence = new SortedSet<StationModel>(list),
                                                        Index = seqIndex
                                                    });
                        }
                        else
                        {
                            globalSequences.Add(new InternationalSequenceModel(this)
                                                    {
                                                        Sequence = new SortedSet<StationModel>(list),
                                                        Index = seqIndex
                                                    });
                        }
                    }
                    else
                    {
                        globalSequences.Add(new MixedSequenceModel(this)
                                                {Sequence = new SortedSet<StationModel>(list), Index = seqIndex});
                    }
                    seqIndex++;
                }
            }
            _globalSequences = globalSequences;
        }

        #endregion

        #region IEquatable<ConnectionModel> Members

        public bool Equals(ConnectionModel other)
        {
            return other.AllStations.All(stationModel => AllStations.Contains(stationModel));
        }

        #endregion
    }
}